﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;

namespace Dxr.Data.SqlServer
{
    public class SqlServerDataAccess : ObjectAccess
    {
        #region 字段
        private Dictionary<string, DbType> _dbTypes;
        #endregion

        #region 构造函数
        public SqlServerDataAccess()
            : base(SqlClientFactory.Instance)
        {
        }
        #endregion

        #region 属性
        private Dictionary<string, DbType> DbTypes
        {
            get
            {
                if (_dbTypes == null)
                    _dbTypes = Enum.GetValues(typeof(DbType)).Cast<DbType>().ToDictionary(p => p.ToString().ToUpper(), p => p);
                return _dbTypes;
            }
        }
        #endregion

        #region 重写方法
        protected override string CreateSelectSql(CreateSelectSqlParameter parameter)
        {
            var sql = "";
            if (parameter.Paging != null && parameter.Paging.PageSize > 0)
            {
                if (parameter.Paging.PageIndex < 1)
                    parameter.Paging.PageIndex = 1;
                sql = string.Format("SELECT TOP {0} row_number() {1} rownum, {2} FROM {3}"
                    , parameter.Paging.PageSize * parameter.Paging.PageIndex
                    , string.IsNullOrEmpty(parameter.Orderby) ? "OVER (order by (select 0))" : string.Format("OVER ({0})", parameter.Orderby)
                    , string.Join(",", parameter.Columns.Select(p => p.ToSelectColumn()))
                    , parameter.Info.GetTableName());

                sql += AddTail(parameter);

                sql = string.Format("select temp.* from ({0}) temp where temp.rownum>{1}", sql, parameter.Paging.PageSize * (parameter.Paging.PageIndex - 1));
            }
            else
            {
                sql = string.Format("SELECT {0} FROM {1}", string.Join(",", parameter.Columns.Select(p => p.ToSelectColumn())), parameter.Info.GetTableName());

                sql += AddTail(parameter);

                if (!string.IsNullOrEmpty(parameter.Orderby))
                    sql += " " + parameter.Orderby;
            }

            return sql;
        }

        protected override string CreateSelectSql(CreateGroupSelectSqlParameter parameter)
        {
            var sql = "";

            if (parameter.Paging != null && parameter.Paging.PageSize > 0)
            {
                if (parameter.Paging.PageIndex < 1)
                    parameter.Paging.PageIndex = 1;
                sql = string.Format("SELECT TOP {0} row_number() {1} rownum ,{2} FROM {3}"
                    , parameter.Paging.PageSize * parameter.Paging.PageIndex
                    , string.IsNullOrEmpty(parameter.Orderby) ? "OVER (order by (select 0))" : string.Format("OVER ({0})", parameter.Orderby)
                    , string.Join(",", parameter.Columns.Select(p => p.ToSelectColumn()))
                    , parameter.Info.GetTableName());

                sql += AddTail(parameter);

                sql = string.Format("select temp.* from ({0}) temp where temp.rownum>{1}", sql, parameter.Paging.PageSize * (parameter.Paging.PageIndex - 1));
            }
            else
            {
                sql = string.Format("SELECT {0} FROM {1}", string.Join(",", parameter.Columns.Select(p => p.ToSelectColumn())), parameter.Info.GetTableName());

                sql += AddTail(parameter);

                if (!string.IsNullOrEmpty(parameter.Orderby))
                    sql += " " + parameter.Orderby;
            }

            string newColumns = string.Empty;
            if (parameter.GroupedSelectColumns != null && parameter.GroupedSelectColumns.Count > 0)
            {
                newColumns = string.Join(",", parameter.GroupedSelectColumns.Select(p =>
                {
                    if (p.ClassInfo == parameter.Info)
                        return p.ToSelectColumn(parameter.NewTableNameEx, p.ToColumn());
                    else
                        return p.ToSelectColumn();
                }));
                sql = string.Format("select {0} from ({1}) {2}", newColumns, sql, parameter.NewTableNameEx);
                if (!string.IsNullOrEmpty(parameter.GroupedJoin))
                    sql += " " + parameter.GroupedJoin;
            }

            return sql;
        }

        private string AddTail(CreateSelectSqlParameter parameter)
        {
            var gParameter = parameter as CreateGroupSelectSqlParameter;

            var sql = "";
            if (!string.IsNullOrEmpty(parameter.Join))
                sql += " " + parameter.Join;
            if (!string.IsNullOrEmpty(parameter.Where))
                sql += " " + parameter.Where;
            if (gParameter != null && !string.IsNullOrEmpty(gParameter.Group))
                sql += " " + gParameter.Group;
            if (gParameter != null && !string.IsNullOrEmpty(gParameter.Having))
                sql += " " + gParameter.Having;

            return sql;
        }

        public override DbParameter CreateParameter(int index, object value, string dbType = null, string name = null, bool isOutPut = false, bool isInOutPut = false, int? size = null, bool isProcedure = false)
        {
            var paramer = new SqlParameter();

            paramer.ParameterName = string.IsNullOrEmpty(name) ? ("p" + index) : name;
            if (!isProcedure)
                paramer.ParameterName = "@" + paramer.ParameterName;

            if (value is byte[])
            {
                paramer.Value = value;
            }
            else
                paramer.Value = SqlExecuter.ConvertValue(value);

            if (!string.IsNullOrEmpty(dbType))
            {
                if (DbTypes.ContainsKey(dbType.ToUpper()))
                    paramer.DbType = DbTypes[dbType.ToUpper()];
            }

            if (size.HasValue)
                paramer.Size = size.Value;
            if (isOutPut)
                paramer.Direction = ParameterDirection.Output;
            else if (isInOutPut)
                paramer.Direction = ParameterDirection.InputOutput;

            return paramer;
        }
        #endregion

        protected override Dictionary<string, ColumnInfo> CreateColumnInfo(IEnumerable<string> columns, ClassInfo root)
        {
            return SqlServerColumnInfo.Create(columns, root);
        }

        protected override ColumnInfo CreateColumnInfo(string original)
        {
            return new SqlServerColumnInfo(original);
        }

        protected override ClassInfo CreateClassInfo(string @as, Mapping.ClassNode classNode)
        {
            return new SqlServerClassInfo(@as, classNode);
        }

        protected override string GetProcedureName(Mapping.ProcedureNode procedureNode)
        {
            return procedureNode.GetProcedureName(false);
        }
    }
}
